<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
      body {
        background-color: #ffffff;
        display: flex;
        justify-content: center;
        align-items: center;
        flex-direction: column;
      }
      * {
        margin: 0;
        padding: 0;
      }
      canvas {
        /* border-bottom: 1px solid #266fff; */
      }
    </style>
  </head>
  <body>
    <canvas></canvas>
    <audio src="../mp3/0.mp3" controls></audio>
  </body>
  <script>
    const audioEle = document.querySelector('audio')
    const cvs = document.querySelector('canvas')
    const ctx = cvs.getContext('2d')
    // 初始化canvas尺寸
    function initCvs() {
      cvs.width = window.innerWidth * devicePixelRatio
      cvs.height = (window.innerHeight / 2) * devicePixelRatio
    }
    initCvs()
 
    // 初始化，只需要做一次就可以了
    let isInit = false
    let dateArray = null
    let analyser = null
 
    audioEle.addEventListener('play', function (e) {
      if (isInit) return
      // 初始化
      const audCtx = new AudioContext() // 创建音频上下文
      const source = audCtx.createMediaElementSource(audioEle) // 创建音频源节点
      // 什么叫音频源节点？节点其实就是音频处理的一个环节。音频可能有很多环节 比如说修音 比如说混响 比如说把音调高调低这些都是处理环节
      // 每一个环节就是一个节点 在这些节点当中有一种叫源节点 表示我们音频数据的来源
      analyser = audCtx.createAnalyser()
      analyser.fftSize = 512 // 2的n次幂。数值越大越细腻
      // 创建数组 用于接收分析器节点的分析数据
      dateArray = new Uint8Array(analyser.frequencyBinCount) // 数组里面的每一项都是一个无符号的8位整数
      source.connect(analyser)
      analyser.connect(audCtx.destination)
      isInit = true
    })
 
    // 把分析出的波形绘制到canvas上
    function draw() {
      requestAnimationFrame(draw)
      // 清空画布
      const { width, height } = cvs
      ctx.clearRect(0, 0, width, height)
      if (!isInit) return
      // 让分析器节点分析出数据到数组中
      analyser.getByteFrequencyData(dateArray)
      const len = dateArray.length / 2.5
      ctx.fillStyle = '#266fff'
      const barWidth = width / len / 2
      for (let i = 0; i < len; i++) {
        const data = dateArray[i] // < 256
        const barHeight = (data / 255) * height
        const x1 = i * barWidth + width / 2
        const x2 = width / 2 - (i + 1) * barWidth
        const y = height - barHeight
        ctx.fillRect(x1, y, barWidth - 2, barHeight)
        ctx.fillRect(x2, y, barWidth - 2, barHeight)
      }
    }
    draw()
  </script>
</html>